from mysite.config.django_setup import *
from django.db import models
from datetime import datetime
import json
import logging
from django.db import transaction
import lqu

logger = logging.getLogger('lq_fucker')


class Strategy(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20, unique=True, verbose_name='名称')
    ver = models.CharField(max_length=20)
    sort_key = models.IntegerField(default=0)

    def __str__(self):
        return 'Strategy: ' + self.name

    def get_latest_result(self):
        return dict(self.strategyresult_set.filter(latest=True).values_list('symbol', 'percent'))

    def get_prev_result(self):
        return dict(self.strategyresult_set.filter(latest=False).values_list('symbol', 'percent'))

    class Meta:
        verbose_name_plural = 'strategies'
        db_table = 'lqcon_strategy'


class StrategyResult(models.Model):
    strategy = models.ForeignKey(Strategy, on_delete=models.CASCADE)
    symbol = models.CharField(max_length=20, verbose_name='标的')
    percent = models.FloatField(verbose_name='比例')
    ts = models.DateTimeField()
    last_updated = models.DateTimeField(auto_now=True)
    latest = models.BooleanField(default=True)

    class Meta:
        db_table = 'lqcon_strategyresult'


class StrategyGroup(models.Model):
    group_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20, unique=True)
    sort_key = models.IntegerField(default=0)
    strategy = models.ManyToManyField(Strategy, through='StrategyPctInGroup')

    def __str__(self):
        return 'StrategyGroup: ' + self.name

    def get_latest_result(self):
        res = {}
        for sp in StrategyPctInGroup.objects.filter(strategy_group=self):
            for symbol, pct in sp.strategy.get_latest_result().items():
                if symbol not in res.keys():
                    res[symbol] = 0.0
                res[symbol] += pct * sp.strategy_pct
        return res

    def get_prev_result(self):
        res = {}
        for sp in StrategyPctInGroup.objects.filter(strategy_group=self):
            for symbol, pct in sp.strategy.get_prev_result().items():
                if symbol not in res.keys():
                    res[symbol] = 0.0
                res[symbol] += pct * sp.strategy_pct
        return res

    class Meta:
        db_table = 'lqcon_strategygroup'


class StrategyPctInGroup(models.Model):
    strategy = models.ForeignKey(Strategy, on_delete=models.CASCADE)
    strategy_group = models.ForeignKey(StrategyGroup, on_delete=models.CASCADE, related_name='strategies')
    strategy_pct = models.FloatField()

    class Meta:
        db_table = 'lqcon_strategypctingroup'


class Product(models.Model):
    product_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20, unique=True, verbose_name='名称')
    aname = models.CharField(max_length=50, verbose_name='简称', default='--')
    shares = models.FloatField()

    sort_key = models.SmallIntegerField()
    last_close = models.FloatField()

    @property
    def nav(self):
        return sum([account.nav for account in self.account_set.all()])

    @property
    def nav_per_share(self):
        return self.nav / self.shares

    @property
    def pct_change(self):
        return self.nav / self.last_close - 1

    @property
    def pct_change_str(self):
        return '{:.2%}'.format(self.pct_change)

    @transaction.atomic
    def purchase(self, value, account=None):
        if account is None:
            account = self.account_set.get()
            self.shares += value / self.nav_per_share
            account.cash += value
            account.save()
            self.last_close += value
            self.save()
        else:
            account = self.account_set.get(name=account)
            self.shares += value / self.nav_per_share
            account.cash += value
            account.save()
            self.last_close += value
            self.save()

    def __str__(self):
        return 'Product: ' + self.name

    class Meta:
        db_table = 'lqcon_product'


class Account(models.Model):
    account_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20, unique=True, verbose_name='名称')
    aname = models.CharField(max_length=50, verbose_name='简称', default='--')
    gateway_type = models.CharField(max_length=20, verbose_name='连接')
    fucker_type = models.CharField(max_length=20, verbose_name='fucker')
    leverage = models.FloatField(verbose_name='杠杆系数')
    use_global_leverage_coef = models.BooleanField(verbose_name='使用全局系数', default=False)
    cash = models.FloatField()
    available = models.BooleanField(verbose_name='生效', default=False)
    strategy_group = models.ForeignKey(StrategyGroup, on_delete=models.DO_NOTHING, verbose_name='策略组')

    product = models.ForeignKey(Product, on_delete=models.DO_NOTHING, null=True, blank=True)

    additional_data_json = models.TextField(default='{}')

    connection_info_json = models.TextField(default='{}')
    fucking_group = models.CharField(max_length=20, verbose_name='交易组', default='')

    @property
    def connection_info(self):
        return json.loads(self.connection_info_json)

    @connection_info.setter
    def connection_info(self, data):
        self.connection_info_json = json.dumps(data)

    @property
    def additional_data(self):
        return json.loads(self.additional_data_json)

    @additional_data.setter
    def additional_data(self, data):
        self.additional_data_json = json.dumps(data)

    @property
    def nav(self):
        pos_set = dict(self.position_set.values_list('symbol', 'volume'))
        price = lqu.get_current_price(list(pos_set.keys()))
        res = 0.0
        for symbol in pos_set.keys():
            if lqu.symbol_type(symbol) == 'future':
                res += pos_set[symbol] * price[symbol] * lqu.get_contract_multiplier(symbol)
            elif lqu.symbol_type(symbol) == 'stock':
                res += pos_set[symbol] * price[symbol]
        return res + self.cash

    def __str__(self):
        return 'Account: ' + self.name

    @transaction.atomic
    def long(self, symbol, volume, price):
        if lqu.symbol_type(symbol) == 'future':
            multiplier = lqu.get_contract_multiplier(symbol)
        else:
            multiplier = 1
        mv = price * volume * multiplier

        position, _ = self.position_set.get_or_create(symbol=symbol, defaults={'account': self, 'volume': 0,
                                                                               'create_time': datetime.now()})
        position.volume += volume

        if position.volume == 0:
            position.delete()
        else:
            position.save()
        self.cash -= mv
        self.save()

    @transaction.atomic
    def adjust_to_target_percent(self, percent):
        nav = self.nav
        self.position_set.all().delete()
        self.cash = nav
        self.save()
        price = lqu.get_current_price(list(percent.keys()))
        for symbol in percent.keys():
            self.long(symbol, nav * percent[symbol] / price[symbol], price[symbol])

    @property
    def final_leverage_coef(self):
        g_coef = 1.0
        if self.use_global_leverage_coef:
            g_coef = GlobalVariable.objects.get(name='global_leverage_coef').value
        return self.leverage * float(g_coef)

    def get_latest_result(self):
        res = self.strategy_group.get_latest_result()
        for symbol in res.keys():
            res[symbol] *= self.final_leverage_coef
        return res

    def get_prev_result(self):
        res = self.strategy_group.get_prev_result()
        for symbol in res.keys():
            res[symbol] *= self.final_leverage_coef
        return res

    class Meta:
        db_table = 'lqcon_account'


class AccountBroker(models.Model):
    account = models.ForeignKey(Account, on_delete=models.CASCADE)
    balance = models.FloatField(null=True)
    available = models.FloatField(null=True)
    last_updated = models.DateTimeField(auto_now=True)

    @property
    def frozen(self):
        return self.balance - self.available

    @property
    def used_pct(self):
        return 1 - self.available / self.balance

    class Meta:
        db_table = 'lqcon_accountbroker'


class Position(models.Model):
    id = models.AutoField(primary_key=True)
    account = models.ForeignKey(Account, on_delete=models.CASCADE)
    symbol = models.CharField(max_length=20, verbose_name='标的')
    volume = models.FloatField(verbose_name='持仓',default=0)
    create_time = models.DateTimeField()

    class Meta:
        unique_together = [['account', 'symbol']]
        db_table = 'lqcon_position'


class SchedulerStatus(models.Model):
    name = models.CharField(max_length=40, verbose_name='名称', primary_key=True)
    status = models.CharField(max_length=20, verbose_name='状态')
    terminate = models.IntegerField(max_length=2, verbose_name='是否中断')

    last_updated = models.DateTimeField(auto_now=True, verbose_name='更新时间')
    last_err = models.CharField(max_length=100, verbose_name='上次错误')

    ALIVE_INTERVAL = 120

    @property
    def alive(self):
        return (datetime.now() - self.last_updated).seconds < SchedulerStatus.ALIVE_INTERVAL

    class Meta:
        db_table = 'lqcon_schedulerstatus'

class TaskStatus(models.Model):
    name = models.CharField(max_length=40, verbose_name='名称', primary_key=True)
    status = models.CharField(max_length=20, verbose_name='状态')
    last_updated = models.DateTimeField(auto_now=True, verbose_name='上次执行时间')
    last_err = models.CharField(max_length=100, verbose_name='上次错误')

    class Meta:
        db_table = 'lqcon_taskstatus'



class GlobalVariable(models.Model):
    name = models.CharField(max_length=40, verbose_name='名称', primary_key=True)
    value = models.FloatField()

    class Meta:
        db_table = 'lqcon_globalvariable'


class TradeRecord(models.Model):
    id = models.AutoField(primary_key=True)
    symbol = models.CharField(max_length=50, verbose_name='标的')
    exchange = models.CharField(max_length=10, verbose_name='交易所简称')
    orderid = models.CharField(max_length=50, verbose_name='订单')
    tradeid = models.CharField(max_length=20, verbose_name='交易ID')
    direction = models.CharField(max_length=20, verbose_name='交易方向，多，空')
    offset = models.CharField(max_length=20, verbose_name='开平')
    price = models.FloatField(verbose_name='价格')
    volume = models.FloatField(verbose_name='交易数量')
    datetime = models.DateTimeField(verbose_name='日期', db_index=True)
    account_name = models.CharField(max_length=50, verbose_name='账号名称')

    class Meta:
        db_table = 'lqcon_traderecord'
